package SSIHoSEHtmlChannel;
use strict;
use vars qw /@ISA/;
use XML::Twig;
use HTML::TreeBuilder;
use Data::Dumper;
use Text::Trim;
use Date::Manip;
use LWP::UserAgent;
use POSIX qw(strftime);
use Encode;
use StockInfoSet;
use StockInfo;
use MarketInfo;
use ChannelInfo; 
use DataCollectionChannel;
@ISA = ("DataCollectionChannel");

sub new{
	my $class = shift;
	my $self = {};
	$self->{URL} = 'http://ssi.com.vn/BangGia/Hose/Data/Data.htm';
	$self->{STOCK_FIELD_FORMAT} = [ 'Symbol', 'CeilingPrice', 'FloorPrice', 'RefPrice', 'FRoom', 'FBuy', 
									'SS1MatchPrice', 'SS1MatchVolume', 'HighestPrice', 'LowestPrice', 'SS2MatchPrice', 'SS2MatchVolume', 
									'TempPrice', 'TempVolume', 'ChangedPrice',
									'Best3BidPrice', 'Best3BidVolume', 'Best2BidPrice', 'Best2BidVolume', 'Best1BidPrice', 'Best1BidVolume',
									'Best1OffPrice', 'Best1OffVolume', 'Best2OffPrice', 'Best2OffVolume', 'Best3OffPrice', 'Best3OffVolume',
									'Description' ];
									
	$self->{MARKET_FIELD_FORMAT} = [ 'Index', 'ChangeIndex', 'TotalVolume', 'TotalValue', 'Advances', 
									 'NoChange', 'Declines', 'TradingDate', 'Time', 'SessionNo', 'NP_V', 'NV_V', 'PrevIndex', 'Transactions', 'ChangePercent'];
	$self->{MARKET_FIELD_ALIAS_FORMAT} = [ 'VNIndex', 'ChangeIndex', 'TotalVol', 'TotalValue', 'Advances', 
									 'NoChange', 'Declines', 'Date', 'Clock', 'SessionNo', 'NP_V', 'NV_V', 'PrevIndex', 'Transactions', 'ChangePercent' ];				 

	$self->{SECURITY_CENTER_CODE} = 'HoSE';	
	bless $self, $class;
	return $self;
}


##################################
##	get html page of given url	##
##################################
sub get_data_source{
	my $self = shift;
	my $browser = LWP::UserAgent->new(
	  	agent => 'Mozilla/5.0 (compatible; MSIE 4.01; Windows95)',
	  	timeout =>	$self->{TIMEOUT} 	
	 );	
	$browser->parse_head(0);	  
 	my $resp = $browser->get($self->{URL});
 	return ($resp->content, $resp->is_success, $resp->status_line);   	
}

##################################
## get stock info set			##
## input:						##
##			sys_seq				##
## output:						##
##		a StockInfoSet object	##
##################################
sub get_stock_info_set{
	my $self = shift;
	my $content;
	my $is_success;
	my $status_line;
	my $proc_time;
	my $channel_name = 	$self->{NAME};
	my $start_time = time();
	my $stock_info_set = StockInfoSet::new();
	
	# assign sys sequence, start_time and name to channelinfo object
	$stock_info_set->{CHANNEL_INFO}->set('SYS_SEQ', shift);
	$stock_info_set->{CHANNEL_INFO}->set('NAME', $channel_name);
	$stock_info_set->{CHANNEL_INFO}->set('START_TIME', $start_time);
	# assign priority
	$stock_info_set->{MARKET_INFO}->set('PRIORITY', $self->{PRIORITY});
	
	# get data source from url 
	($content, $is_success, $status_line) = $self->get_data_source();
	
	if (!$is_success){
		$self->{STATUS}->{CODE} = 'TIMEOUT';
		$self->{STATUS}->{MSG} = 'Can\'t fetch data source(connection timeout)';
		# caculate processing time 
		$proc_time = time - $start_time;
		$stock_info_set->{CHANNEL_INFO}->set('PROCESSING_TIME', $proc_time);
		$stock_info_set->{CHANNEL_INFO}->set('STATUS', $self->{STATUS}->{CODE});
		$stock_info_set->{CHANNEL_INFO}->set('MESSAGE', $self->{STATUS}->{MSG});
		$stock_info_set->{MARKET_INFO} = undef;
		$stock_info_set->{STOCK_INFO_SET} = undef;
		return ($stock_info_set,$self->{STATUS});			
	}else{
		$self->{STATUS}->{CODE} = 'OK';
		$self->{STATUS}->{MSG} = "Data source is fetched($status_line)";					
	}
	
	# get the market stock_info chunk and market_info chunk from the data source
	my $root = HTML::TreeBuilder->new_from_content(Encode::decode_utf8($content));
	my @tables = $root->find_by_tag_name('table');
	my $changed_percent;
	foreach my $table (@tables){
		if($table->id() =~ /Index/){ # the market info table
			my @tds = $table->find_by_tag_name('td');
			foreach my $td (@tds){
				for(my $i = 0; $i < (@{$self->{MARKET_FIELD_FORMAT}}); $i++){
					my $field = @{$self->{MARKET_FIELD_FORMAT}}[$i];
					my $field_alias = @{$self->{MARKET_FIELD_ALIAS_FORMAT}}[$i];					
					if( $td->id() =~ /$field_alias/){
						my $field_value = $td->as_text();
						if($field eq 'ChangeIndex'){
							$field_value =~ /(.*)\((.*)\%\)/;
							$field_value = $1 ;
							$changed_percent = $2;					
						};
						$field_value =~ s/,//g;
						# Volume unit is 10 so if the field is a volume, multiply it with 10
						$field_value *= 10 if($field =~ /.*Volume/);
						$stock_info_set->{MARKET_INFO}->set($field, $field_value);
					}
				}
			}
			if(!ref($stock_info_set->{MARKET_INFO}->get('ChangePercent'))){
					$stock_info_set->{MARKET_INFO}->set('ChangePercent', $changed_percent); 
			}
			
			if($stock_info_set->{MARKET_INFO}->get('TradingVolume')){
					$stock_info_set->{MARKET_INFO}->set('TradingVolume', 10 * $stock_info_set->{MARKET_INFO}->get('TradingVolume') ); 
			}
	        # set market date
           	$stock_info_set->{MARKET_INFO}->set('TradingDate', strftime("%d/%m/%Y", localtime($stock_info_set->start_time())));
			# set market time based on the report from SSI
			$stock_info_set->{MARKET_INFO}->set('Time', $stock_info_set->{MARKET_INFO}->get('Time') . ":00");
			
			# set market time based on the delay time
			if($self->{DELAY_TIME}){
				my $market_time = DateCalc(ParseDate("today"), "- " . $self->{DELAY_TIME} . " seconds");			
				$stock_info_set->{MARKET_INFO}->set('Time', UnixDate($market_time, "%H:%M:%S"));						
			}else{
				$stock_info_set->{MARKET_INFO}->set('Time', undef);			
			}
			# set the previous index
			if( $changed_percent ne '0' ){
				$stock_info_set->{MARKET_INFO}->set('PrevIndex',sprintf('%0.2f', 100*($stock_info_set->{MARKET_INFO}->get('ChangeIndex') / $changed_percent )));
			}else{
				$stock_info_set->{MARKET_INFO}->set('PrevIndex', 0);
			}
			
			$stock_info_set->{MARKET_INFO}->set('CHANNEL_NAME', $stock_info_set->{CHANNEL_INFO}->get('NAME'));
			$stock_info_set->{MARKET_INFO}->set('SYS_SEQ', $stock_info_set->{CHANNEL_INFO}->get('SYS_SEQ'));
			$stock_info_set	->{MARKET_INFO}->set('SECURITY_CENTER', $self->{SECURITY_CENTER_CODE});
			
			foreach my $field (@{$self->{MARKET_FIELD_FORMAT}}){
				if(!$stock_info_set->{MARKET_INFO}->get($field)){
					$stock_info_set->{MARKET_INFO}->set($field, undef);
			}
			}
		}elsif($table->id() =~ /Price/){ # the quote price table			
			my @trs = $table->find_by_tag_name('tr');
			foreach my $tr (@trs){				
				my @tds = $tr->content_list();				
				if($tr->as_text() eq ''){	# abort <tr> element that has no child							
					next;
				}else{	
					my $stock_info = StockInfo::new('ObjectInfo');
					for(my $i=0; $i < @{$self->{STOCK_FIELD_FORMAT}}; $i++){
						my $field = $self->{STOCK_FIELD_FORMAT}->[$i];
						if($i < @tds){
							my $field_value = $tds[$i]->as_text();
							$field_value =~ s/\&lt\;/</g;
							$field_value =~ s/\&gt\;/>/g;
							$field_value =~ s/\&amp\;//g;
							$field_value =~ s/nbsp\;//g;
							$field_value =~ s/\&nbsp\;//g;
							$field_value =~ s/<br>/ /g;													
							if($field eq 'ChangedPrice' && $field_value =~ /.*([+,-].*)/){
								$field_value = $1; 
							};						
							$field_value =~ s/,//g;
							if($field =~ /.*Price/ ){
								$field_value = -1 if($field_value eq 'ATO');
								$field_value = -2 if($field_value eq 'ATC');
							}
							$stock_info->set($field, $field_value);											
						}else{
							$stock_info->set($field, undef);
						}
						
					}					
					$stock_info->set('CHANNEL_NAME', $stock_info_set->{CHANNEL_INFO}->get('NAME'));
					$stock_info->set('SYS_SEQ', $stock_info_set->{CHANNEL_INFO}->get('SYS_SEQ'));	
					push @{$stock_info_set->{STOCK_INFOS}}, $stock_info;
				}
			}
		}
	}
	# free the root tree
	$root->delete();
		
	
	if(ref($stock_info_set->{MARKET_INFO}) && ref($stock_info_set->{STOCK_INFOS})){
		$self->{STATUS}->{CODE} = 'SUCCEEDED';
		$self->{STATUS}->{MSG} = 'Parsed successfully ';		
	}else{
		$self->{STATUS}->{CODE} = 'FAILED';
		$self->{STATUS}->{MSG} = 'Failed to parse';		
	}	
	# caculate processing time 
	$proc_time = time - $start_time;
	$stock_info_set->{CHANNEL_INFO}->set('PROCESSING_TIME', $proc_time);
	$stock_info_set->{CHANNEL_INFO}->set('STATUS', $self->{STATUS}->{CODE});
	$stock_info_set->{CHANNEL_INFO}->set('MESSAGE', $self->{STATUS}->{MSG});	
	return ($stock_info_set,$self->{STATUS});	
}
##################################
## get market time				##
## input:						##
## output:						##
##		{HOUR => $hour 			##
##		 MIN => $time }			##
##################################
sub get_market_time{
	my $self = shift;
	my $content;
	my $is_success;
	my $status_line;
	my $start_time = time();
	my $channel_name = $self->{NAME};
	
	# get data source from url 
	($content, $is_success, $status_line) = $self->get_data_source();
	
	
	if (!$is_success){
		$self->{STATUS}->{CODE} = 'TIMEOUT';
		$self->{STATUS}->{MSG} = 'Can\'t fetch data source(connection timeout)';
		return (undef,$self->{STATUS});			
	}else{
		$self->{STATUS}->{CODE} = 'OK';
		$self->{STATUS}->{MSG} = "Data source is fetched($status_line)";					
	}
	
	# get the market stock_info chunk and market_info chunk from the data source
	$content =~ /.*<td id=Clock>(.*)<\/td><td id=SessionNo>.*/;
	if($1){
		my ($hour,$minute) = split(":",$1);
		$self->{STATUS}->{CODE} = 'OK';
		$self->{STATUS}->{MSG} = "Time is fetched| Channel $channel_name: market openned";
		return({HOUR => $hour, MIN => $minute}, $self->{STATUS});		
	}else{
		$self->{STATUS}->{CODE} = 'IVLFORMAT';
		$self->{STATUS}->{MSG} = "Can't get the market time| Channel $channel_name: data in a invalid format";	
		return (undef, $self->{STATUS});	
	}
}

#####################################
## wait until the market is opened ##
#####################################

sub get_market_status{
	my $self = shift;
	my $market_start_time = shift;
	my $market_stop_time = shift;
	my $market_time;
	my $market_status = {
		CODE => "CLOSING",
		MSG => ""
	};
		
	# if the center is HoSE, check real time
	my ($market_time_tmp, $status) = $self->get_market_time();
	if(!ref($market_time_tmp)){
		return $status;
	}
	my $hour = $market_time_tmp->{HOUR};
	my $min = $market_time_tmp->{MIN};
	$market_time = ParseDate($hour . ':' . $min . ':00');			
	$market_status->{MSG} = 'Market time: ' . $market_time_tmp->{HOUR} . ':' . $market_time_tmp->{MIN} ;
	if(Date_Cmp($market_time, $market_start_time) >= 0 && Date_Cmp($market_time, $market_stop_time) <= 0){
		$market_status->{CODE} = 'OPENING';
	}else{
		$market_status->{CODE} = 'CLOSING';
	}
	return $market_status;
}
sub DESTROY{	
	my $self = shift;
	$self->{BROWSER} = undef;
}
1;
